<?php
namespace Goatherd\Content\Engine\Reader\Buffer;

use Goatherd\Content\Engine\Resource\IResource;

/**
 * Very basic file based mapper.
 *
 * @note can only store string data.
 *
 * @todo might be useful to defer file system reads
 * @todo refactor to use IBuffer interface.
 *
 * @category ContentEngine
 * @package Goatherd\Content\Engine\Reader
 * @subpackage Mapper
 */
class File
implements IBuffer
{
    /**
     *
     * @var string
     */
    protected $_path = null;

    /**
     * Will use subdirectories for names longer than $_chunkSize byte.
     * The property is applied recurringly.
     *
     * @var string
     */
    protected $_chunkSize = 5;

    /**
     * Suffix to resource filename that determines the place for meta values.
     *
     * @var string
     */
    protected $_metaSuffix = '.meta';

    /**
     *
     * @param string $path
     * @throws Exception
     */
    public function setPath($path)
    {
        $path = realpath($path);
        if (!is_string($path) || !is_dir($path) || !is_readable($path)) {
            throw new Exception('$path must hold a readable directory for ' . __CLASS__);
        }

        $this->_path = $path;
    }

    /**
     *
     * @return string
     */
    public function getPath()
    {
        return $this->_path;
    }

    /**
     * Will use base64 to sanitise file names and use subdirectories if needed.
     *
     * @param mixed $identifier
     */
    protected function _idToPath($identifier)
    {
        is_string($identifier) or $identifier = serialize($identifier);
        $identifier = base64_encode($identifier);

        $dirs = str_split($identifier, $this->_chunkSize);
        $filename = $this->_path . DIRECTORY_SEPARATOR . implode(DIRECTORY_SEPARATOR, $dirs);

        return $filename;
    }

    /**
     *
     * @param string $path
     * @return string
     */
    protected function _pathToMeta($path)
    {
        return $path . $this->_metaSuffix;
    }

    /**
     * (non-PHPdoc)
     * @see Goatherd\Content\Engine\Reader.IReaderMapper::hasResource()
     */
    public function hasResource($identifier)
    {
        $filename = $this->_idToPath($identifier);
        return is_readable($filename) && is_file($filename);
    }

    /**
     * (non-PHPdoc)
     * @see Goatherd\Content\Engine\Reader.IReaderMapper::getResource()
     */
    public function getResource($identifier)
    {
        $filename = $this->_idToPath($identifier);
        $metaFilename = $this->_pathToMeta($filename);

        if (!is_readable($filename) || !is_readable($metaFilename)) {
            throw new Exception('Can not access (meta) file for resource.');
        }

        $content = file_get_contents($filename);
        $meta = unserialize(file_get_contents($metaFilename));

        $resource = new Resource($identifier);
        $resource->setContent($content);
        $resource->setMetaArray($meta);

        return $resource;
    }

    /**
     * (non-PHPdoc)
     * @see Goatherd\Content\Engine\Reader.IReaderMapper::setResource()
     */
    public function setResource($identifier, IResource $data)
    {
        $filename = $this->_idToPath($identifier);
        $metaFilename = $this->_pathToMeta($filename);

        $path = dirname($filename);

        if (!is_writable($path) && (!is_writable($filename) || !is_writable($metaFilename))) {
            throw new Exception('Can not access (meta) file for resource.');
        }

        $content = $data->getContent();
        $success = false !== file_put_contents($filename, $content);
        if ($success) {
            $meta = serialize($data->getMetaArray());
            $success = false !== file_put_contents($metaFilename, $meta);
        }

        return $success;
    }
}